package xyj.sparrow.client.handler;

import xyj.sparrow.common.netty.bean.Response;
import io.netty.channel.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import xyj.sparrow.client.NettyClient;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @ClassName IdleClientHandler
 * @Description TODO
 * @Author Jim
 * @Date 2021/1/19 17:05
 * @Version 1.0.0
 **/
@Slf4j
public class IdleClientHandler extends ChannelInboundHandlerAdapter {
    private final NettyClient nettyClient;
    /**
     * 重连阈值
     **/
    private static final int RECONNECTION_THRESHOLD = 3;
    /**
     * 连续读空闲 计数
     **/
    private final AtomicInteger times = new AtomicInteger(0);
    private static final Object PING = "PING";
    private static final Object PONG = "PONG";

    public IdleClientHandler(NettyClient nettyClient) {
        this.nettyClient = nettyClient;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        times.set(0);
        if (!Response.class.isAssignableFrom(msg.getClass())) {
            log.info("client receive idle : " + msg);
            if (PING.equals(msg)) {
                sendPong(ctx);
            }
        }
        ctx.fireChannelRead(msg);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        log.info("掉线了");
        //重新连接
        boolean isSuccess = nettyClient.getIsActive().compareAndSet(true, false);
        if (isSuccess) {
            log.info("channelInactive抢到重连权");
            reconnect(ctx);
        }
        try {
            super.channelInactive(ctx);
        } catch (Exception e) {
            log.error("", e);
        }
    }


    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (!IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
            return;
        }
        IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
        if (!idleStateEvent.state().equals(IdleState.READER_IDLE)) {
            return;
        }
        int idleTimes = times.addAndGet(1);
        if (idleTimes >= RECONNECTION_THRESHOLD) {
            log.info("三次连续读空闲，将重新连接服务器");
            boolean isSuccess = nettyClient.getIsActive().compareAndSet(true, false);
            if (isSuccess) {
                log.info("userEventTriggered抢到重连权");
                reconnect(ctx);
            }
        } else {
            log.info("第" + idleTimes + "次读空闲");
            sendPing(ctx);
        }
    }

    private void reconnect(ChannelHandlerContext ctx) {
        ctx.channel().close();
        final EventLoop eventLoop = ctx.channel().eventLoop();
        eventLoop.schedule(nettyClient::connect, 1L, TimeUnit.SECONDS);
    }

    private void sendPong(ChannelHandlerContext ctx) {
        log.info("client send idle : " + PONG);
        ctx.channel().writeAndFlush(PONG);
    }

    private void sendPing(ChannelHandlerContext ctx) {
        log.info("client send idle : " + PING);
        ctx.channel().writeAndFlush(PING);
    }
}
